package org.korifey.ffbdd.api.op;

public enum OperationEnum {
	
	
	// UNARY
	NOT		(new Not()),
	UMINUS	(new UMinus()),
	
	//Common binary
	EQ		(new Equals()),
	NEQ		(new NotEquals()),
	
	// Boolean binary
	AND 	(new And()),
	OR	 	(new Or()),
	IMPL 	(new Impl()),
	
	//Conditional 
	IF		(new If()),
	ELSE	(new Else()),
	NULL_REPLACE (new NullReplace()),
	
	//Integer binary
	ADD		(new Add()),
	SUB		(new Sub()),
	MUL		(new Mul()),
	MOD		(new Mod()),
	DIV		(new Div());
	
	
	public static Object NULL = new Object();
	
	private IOperation op;

	private OperationEnum(IndexedOperation op) {
		op.setCode(ordinal());
		this.op = op;
	}

	public IOperation getOperation() {
		return op;
	}
	
	public int code() {
		return getOperation().code();
	}
	
	
	
	//Various default indexed operations
	
	//Unary operations
	static class Not extends IndexedOperation implements IUnaryOperation<Boolean, Boolean> {
		public Boolean apply(Boolean arg) {
			return ! arg;
		}
	}
	
	static class UMinus extends IndexedOperation implements IUnaryOperation<Integer, Integer> {
		public Integer apply(Integer arg) {
			return -arg;
		}
	}
	
	//Binary common operations
	static class NotEquals<T1, T2> extends IndexedOperation implements IBinaryOperation<T1, T2, Boolean> {
		public Boolean apply(T1 op1, T2 op2) {
			return ! op1.equals(op2);
		}
	}
	
	static class Equals<T1, T2> extends IndexedOperation implements IBinaryOperation<T1, T2, Boolean> {
		public Boolean apply(T1 op1, T2 op2) {
			return op1.equals(op2);
		}
	}
	
	//Binary boolean operations
	static class And extends IndexedOperation implements IBinaryOperation<Boolean, Boolean, Boolean> {
		public Boolean apply(Boolean op1, Boolean op2) {
			return  op1 && op2;
		}
	}
	
	static class Or extends IndexedOperation implements IBinaryOperation<Boolean, Boolean, Boolean> {
		public Boolean apply(Boolean op1, Boolean op2) {
			return  op1 && op2;
		}
	}
	
	static class Impl extends IndexedOperation implements IBinaryOperation<Boolean, Boolean, Boolean> {
		public Boolean apply(Boolean op1, Boolean op2) {
			return  op1 && op2;
		}
	}
	
	//Conditional
	static class If<T> extends IndexedOperation implements IBinaryOperation<Boolean, T, T> {
		public T apply(Boolean op1, T op2) {
			return op1? op2: null;
		}
	}
	static class Else<T> extends IndexedOperation implements IBinaryOperation<Boolean, T, T> {
		public T apply(Boolean op1, T op2) {
			return op1? null : op2;
		}
	}
	static class NullReplace<T> extends IndexedOperation implements IBinaryOperation<T, T, T> {
		public T apply(T op1, T op2) {
			return op1 == null? op2 : op1;
		}
	}
	
	
	//Binary Integer
	static class Add extends IndexedOperation implements IBinaryOperation<Integer, Integer, Integer> {
		public Integer apply(Integer op1, Integer op2) {
			return op1 + op2;
		}
	}
	
	static class Sub extends IndexedOperation implements IBinaryOperation<Integer, Integer, Integer> {
		public Integer apply(Integer op1, Integer op2) {
			return op1 - op2;
		}
	}
	
	static class Mul extends IndexedOperation implements IBinaryOperation<Integer, Integer, Integer> {
		public Integer apply(Integer op1, Integer op2) {
			return op1 * op2;
		}
	}
	
	static class Mod extends IndexedOperation implements IBinaryOperation<Integer, Integer, Integer> {
		public Integer apply(Integer op1, Integer op2) {
			return op1 % op2;
		}
	}
	
	static class Div extends IndexedOperation implements IBinaryOperation<Integer, Integer, Integer> {
		public Integer apply(Integer op1, Integer op2) {
			return op1 / op2;
		}
	}

}	
